/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.commons.runtime.msu;

import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.commons.runtime.FileOperator;
import io.iec.edp.caf.commons.runtime.msu.entities.MsuCommonInfo;
import io.iec.edp.caf.commons.runtime.msu.entities.ServiceUnitYaml;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.io.File;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * 获取Msu配置信息的服务
 * 提供运行时获取的相关信息
 */
public class ServiceUnitConfigService {

    private static final Lock locker = new ReentrantLock();
    //private static List<Map<String,String>> allSuInfo = new ArrayList<>();
    //环境内所有su的名称集合
    private static final List<String> allSu = new ArrayList<>();
    //环境内所有启用的su的名称集合
    private static final List<String> enabledSu = new ArrayList<>();
    //serviceUnit.json集合
    private static final List<MsuCommonInfo> allSuEntity = new ArrayList<>();
    //service-unit.yaml集合
    private static final List<ServiceUnitYaml> allSuYaml = new ArrayList<>();
    //caf_serviceunit.json
    private static String msuJsonConfig;
    //application.yaml todo 读取cloud-config
    private static Map<String,Object> msuYamlConfig;
    private static boolean isInitialized = false;

    private static final Log log = LogFactory.getLog(ServiceUnitConfigService.class);

    /**
     * 获取全部su信息
     * @return List<MsuCommonInfo>
     */
    public static List<MsuCommonInfo> getAllSuInfo() {
        //首次获取进行初始化
        ensureInitialized();

        return allSuEntity;
    }

    /**
     * 获得环境中所有suName
     * @return List<String>
     */
    public static List<String> getAllSu() {
        //首次获取进行初始化
        ensureInitialized();

        return allSu;
    }

    /**
     * 获得caf_serviceunit.json内容
     * @return caf_serviceunit.json
     */
    public static String getMsuJsonConfig() {
        ensureInitialized();

        return msuJsonConfig;
    }

    /**
     * 获取application.yaml内容
     * @return application.yaml Map<String,Object>
     */
    public static Map<String, Object> getMsuYamlConfig() {
        ensureInitialized();

        return msuYamlConfig;
    }

    /**
     * 获取已启用的所有su信息
     * 如果需求的时机较早且不能依赖caf-su时，可以调用此方法
     * @return 所有su信息
     */
    public static List<String> getEnableSu() {
        ensureInitialized();

        // 判断是否分DU启动，DU启动下根据环境变量获取启用su
        String duName = System.getProperty("deployment-unit.name");
        if (duName != null) {
            String property = System.getProperty("deployment-unit.su");
            if (property != null) {
                // 以英文逗号分隔
                String[] array = property.split(",");
                if (array.length == 0) {
                    log.error("Current Deployment-Unit is " + duName + ". There is no msu available, please check your environment.");
                } else {
                    log.info("Current Deployment-Unit is " + duName + ". Enable service units: [" + String.join(",", array) + "]");
                }

                return new ArrayList<>(Arrays.asList(array));
            } else {
                log.error("Current Deployment-Unit is " + duName + ". The System property [deployment-unit.su] is null, please check your environment.");

                return new ArrayList<>();
            }
        }

        return enabledSu;
    }

    /**
     *  初始化配置
     */
    private static void ensureInitialized() {
        if (!isInitialized) {
            try {
                locker.lock();
                if (!isInitialized) {
                    String serverPath = CafEnvironment.getServerRTPath();
                    String appsPath = serverPath + File.separator + "apps";
                    String platformPath = serverPath + File.separator + "platform";
                    //loadAllMsu 读取当前服务器下所有的su信息（包括未启用的）
                    ServiceUnitConfigUtil.getServiceUnitInfo(new File(appsPath), allSuEntity, allSu);
                    ServiceUnitConfigUtil.getServiceUnitInfo(new File(platformPath), allSuEntity, allSu);
                    //验证扫描到su信息
                    //            log.info(allSu);
                    //获取黑白名单配置信息
                    msuJsonConfig = ServiceUnitConfigUtil.loadJsonConfig(serverPath);
                    msuYamlConfig = ServiceUnitConfigUtil.loadYamlConfig(serverPath);

                    enabledSu.addAll(ServiceUnitConfigUtil.getEnableSu(msuYamlConfig, msuJsonConfig, allSu));
                }
            } finally {
                isInitialized = true;
                locker.unlock();
            }
        }
    }

    /**
     * 运行时动态注册su
     *
     * @param suInfos su信息列表
     */
    public static void registerServiceUnitsInfo(List<MsuCommonInfo> suInfos) {
        ensureInitialized();

        try {
            locker.lock();

            List<String> registerSuNames = new ArrayList<>();
            List<MsuCommonInfo> registerInfos = new ArrayList<>();

            for (MsuCommonInfo suInfo : suInfos) {
                if (suInfo == null || suInfo.getName() == null) {
                    throw new RuntimeException("Could not register Msu: null");
                }

                String suName = suInfo.getName();

                //判断su是否重复注册
                boolean needRegister = true;
                if (enabledSu.stream().anyMatch(x -> x.equalsIgnoreCase(suName))) {
                    needRegister = false;
                    log.error(String.format("The ServiceUnit:%s has bean already registered.", suName));
                }

                if (needRegister) {
                    registerSuNames.add(suName);
                    registerInfos.add(suInfo);
                }
            }

            allSu.addAll(registerSuNames);
            allSuEntity.addAll(registerInfos);
            enabledSu.addAll(registerSuNames);
        } finally {
            locker.unlock();
        }
    }

    /**
     * 提供msu相关的标准工具
     * 实时读取
     */
    public static class ServiceUnitConfigUtil{

        private static final String[] baseMsu = {"pfcommon","rtcommon"};
        private static final int MAX_RECURSION_DEPTH = 3; // 设置递归的最大深度

        /**
         * 递归遍历扫描所有的su信息（引入深度）
         * @param fileInfo
         * @param allSuEntity
         * @param allSu
         */
        public static void getServiceUnitInfo(File fileInfo,List<MsuCommonInfo> allSuEntity,List<String> allSu){
            getServiceUnitInfoWithDepth(fileInfo,allSuEntity,allSu,0);
        }

        /**
         * 递归遍历扫描所有的su信息
         * 增加读取service-unit.yaml/yml 2023/4
         * @param fileInfo 扫描路径
         * @param allSuEntity 实体存储
         * @param allSu su名称存储
         */
        public static void getServiceUnitInfoWithDepth(File fileInfo,List<MsuCommonInfo> allSuEntity,List<String> allSu,int depth){
            if (depth >= MAX_RECURSION_DEPTH) {
                return; // 当达到递归限制时，返回
            }
            if (fileInfo != null && fileInfo.exists() && fileInfo.isDirectory()) {
                File[] files = fileInfo.listFiles((dir, name) -> (name != null && name.equalsIgnoreCase(MsuConfigVariable.SU_JSON_FILE)||name.equalsIgnoreCase(MsuConfigVariable.SU_YAML_FILE)||name.equalsIgnoreCase(MsuConfigVariable.SU_YML_FILE)) && dir.isDirectory());
                 //识别为su目录
                if (files != null && files.length > 0) {
                    //if(files.length>1) log.debug("There are complex serviceUnit files. File path is :"+fileInfo.getAbsolutePath());
                    MsuCommonInfo suEntity = null;
                    ServiceUnitYaml suYaml = null;
                    for(File file:files){
                        //识别到ServiceUnit.json
                        if(file.getName().equalsIgnoreCase(MsuConfigVariable.SU_JSON_FILE)){
                            if(suEntity!=null) continue;
                            String info = FileOperator.readToBuffer(file.getAbsolutePath());
                            if (info != null) {
                                //存储suName
                                suEntity = MsuConfigReader.analysisMsuInfo(info);
                                if(suEntity.getApplicationName()==null||suEntity.getApplicationName().length()==0)
                                    suEntity.setApplicationName(fileInfo.getParentFile().getName());
                                //todo 暂时取绝对路径
                                suEntity.setPath(fileInfo.getAbsolutePath());
                            }
                        }else{
                            //识别为yaml文件
                            if(suYaml!=null) continue;
                            suYaml = MsuConfigReader.analysisMsuYamlInfo(FileOperator.getYaml(file.getAbsolutePath()));
                            suEntity = suYaml.resolveMsuCommonInfo();
                            //todo 暂时取绝对路径
                            suEntity.setPath(fileInfo.getAbsolutePath());
                        }
                    }
                    String su = suEntity==null?null:suEntity.getName();
                    if(su!=null&&!allSu.contains(su)&&!allSu.contains(su.toLowerCase())){
                        allSu.add(su);
                        allSuEntity.add(suEntity);
                        //allSuInfo.add(map);
                    }
                } else {
                    //非su目录，继续递归，仅查找目录
                    depth++; // 递归深度加一
                    files = fileInfo.listFiles(pathname -> !pathname.isFile());
                    if (files != null)
                        for (File file : files) {
                            getServiceUnitInfoWithDepth(file,allSuEntity,allSu,depth);
                        }
                }
            }
        }

        /**
         * 读取caf_serviceunit.json文件内容
         */
        public static String loadJsonConfig(String serverPath){
            String suConfigPath = serverPath + File.separator + "config" +
                    File.separator + "runtime" + File.separator + "caf_serviceunits.json";

            return FileOperator.readToBuffer(suConfigPath);
        }

        /**
         * 读取application.yaml中关于su启停的配置
         */
        public static Map<String,Object> loadYamlConfig(String serverPath){
            //String yamlPath = serverPath + File.separator +"runtime" + File.separator +"application.yaml";
            String yamlPath = CafEnvironment.getStartupPath()+System.getProperty("file.separator")+"application.yaml";
            //todo read cloud config
            return FileOperator.getYaml(yamlPath);
        }

        /**
         * 解析启用的su
         * @param yamlConfig yaml黑白名单
         * @param jsonConfig json黑白名单
         * @param allSu 所有su
         * @return 启用su列表
         */
        public static List<String> getEnableSu(Map<String,Object> yamlConfig,String jsonConfig,List<String> allSu){
            //以yaml的优先
            List<String> enableSuList = MsuConfigReader.getEnableSuListFromYaml(yamlConfig,allSu);

            if((enableSuList==null||enableSuList.size()==0)&&jsonConfig!=null){
                enableSuList = MsuConfigReader.getEnableSuListFromJson(jsonConfig,allSu);
            }

            if(enableSuList.contains(null)){
                log.error("Enable service units contains null");
            }
            if(enableSuList.size()==0){
                log.error("There is no msu available, please check your environment.");
            }
            //log.info("enable service units: " + enableSuList);

            log.info("Enable service units: [" + String.join(",", enableSuList) +"]");
            return enableSuList;
        }

        /**
         * 根据指定路径获取所有su信息
         * @param serverPath server路径
         * @return List<MsuCommonInfo>
         */
        public static List<MsuCommonInfo> getAllSuInfo(String serverPath){
            List<MsuCommonInfo> result = new ArrayList<>();
            ServiceUnitConfigUtil.getServiceUnitInfo(new File(serverPath),result,new ArrayList<>());
            return result;
        }


        /**
         * 解析依赖关系
         * @param msuInfos msu信息
         * @return 依赖关系列表
         */
        public static Map<String,Map<String,HashSet<String>>> analyzeMsuDependent(List<MsuCommonInfo> msuInfos){
            //已经解构过依赖关系的su列表
            List<String> finishedList = new ArrayList<>();
            //强依赖的su，但在环境中找不到该su
            List<String> warningDependent = new ArrayList<>();
            //最终的结果
            Map<String,Map<String,HashSet<String>>> result = new HashMap<>();
            //依赖列表
            Map<String,HashSet<String>> parentResult = new HashMap<>();
            //被依赖列表
            Map<String,HashSet<String>> childResult = new HashMap<>();
            //需要处理的su依赖队列
            Map<String,Deque<String>> queues = new HashMap<>();
            for(MsuCommonInfo msuInfo:msuInfos){
                String su = msuInfo.getName();
                if(su==null||"".equals(su)) continue;
                //转为小写方便筛查检索
                List<String> dependenceList = msuInfo.getDependency();
                if(dependenceList==null) dependenceList = new ArrayList<>();
                else dependenceList = dependenceList.stream().map(String::toLowerCase).collect(Collectors.toList());
                //需要识别的列表
                Deque<String> needReadStack =new ArrayDeque<>(dependenceList);
                //转为小写方便筛查检索
                su = su.toLowerCase();
                queues.put(su,needReadStack);
                parentResult.put(su, new HashSet<>());
                childResult.put(su,new HashSet<>());
            }

            //解析依赖
            parentResult.forEach((key,val)->{
                analyzeDependent(key,finishedList,parentResult,queues,warningDependent);
            });

            //反向解析被依赖内容
            parentResult.forEach((key,val)->{
                val.forEach(child->{
                    if(!childResult.containsKey(child))
                        childResult.put(child,new HashSet<>());
                    childResult.get(child).add(key);
                });
                if(!result.containsKey(key)){
                    result.put(key,new HashMap<>());
                }
                result.get(key).put(MsuConfigVariable.SU_PARENT_DEPENDENT,val);
            });
            //保存到一起
            childResult.forEach((key,val)->{
                if(!result.containsKey(key)){
                    result.put(key,new HashMap<>());
                }
                HashSet<String> filterVal=(HashSet<String>) val.stream().filter(n->!key.equals(n)).collect(Collectors.toSet());
                result.get(key).put(MsuConfigVariable.SU_CHILD_DEPENDENT,filterVal);
            });
            //记录一下依赖的su在环境中不存在的信息
            if(warningDependent.size()>0){
                log.error("Can't find dependent msu in environment. Dependent information is: "+warningDependent);
            }
            return result;
        }

        /**
         * 迭代寻找依赖(深搜图依赖)
         * @param su 解析依赖的su
         * @param finishedList 完成的列表
         * @param result 最终的结果
         * @param preparedSus 需要识别的所有队列
         * @param warningDependent 有问题的依赖关系
         */
        private static void analyzeDependent(String su, List<String> finishedList,Map<String, HashSet<String>> result,Map<String,Deque<String>> preparedSus, List<String> warningDependent){
            //已经识别的当前依赖的su 需要确保里面都是环境中存在的su
            HashSet<String> completedSu = result.get(su);
            //先把自己加上防止循环依赖
            completedSu.add(su);

            Deque<String> preparedSu = preparedSus.get(su);
            //开始解析依赖
            while(preparedSu.size()>0){
                String parentSu = preparedSu.pop();
                //su不在列表里 驱逐出去
                if(!preparedSus.containsKey(parentSu)){
                    warningDependent.add(su+"->"+parentSu);
                    continue;
                }
                //如果当前parent还没被识别，再加到识别列表
                if(!completedSu.contains(parentSu)){
                    completedSu.add(parentSu);
                    //如果该su已识别过所有的依赖，直接将结果加入到依赖列表中；否则加入preparedSu中进行解析
                    if(finishedList.contains(parentSu)){
                        completedSu.addAll(result.get(parentSu));
                    }else{
                        preparedSu.addAll(preparedSus.get(parentSu));
                    }
                }
            }

            finishedList.add(su);
            result.put(su,completedSu);
        }

//        /**
//         * 递归寻找依赖(动态规划树依赖)
//         * @param finishedList 已经结束依赖关系的su
//         * @param primaryDependent 初始的依赖关系
//         * @param result 最终结果
//         */
//        private static void analyzeDependent(String su,List<String> finishedList,Map<String,List<String>> primaryDependent,Map<String,HashSet<String>> result){
//            if(!result.containsKey(su))
//                result.put(su,new HashSet<>());
//            List<String> parent = primaryDependent.get(su);
//            if(parent!=null){
//                parent.forEach(parentSu->{
//                    analyzeDependent(parentSu,finishedList,primaryDependent,result);
//                    if(result.containsKey(parentSu))
//                        result.get(su).addAll(result.get(parentSu));
//                    result.get(su).add(parentSu);
//                });
//            }
//            //把平台基础底座加上。
//            result.get(su).addAll(Arrays.asList(baseMsu));
//            finishedList.add(su);
//        }

    }



}
